<?php

namespace Admin\Authentication\Adapter;

use Zend\Authentication\Result as AuthenticationResult;
use Zend\Session\Container as SessionContainer;
use User\Authentication\Adapter\Db as UserDb;
use User\Authentication\Adapter\AdapterChainEvent as AuthenticationEvent;
use Admin\Mapper\AdminUserInterface as AdminMapper;

class Db extends UserDb
{
    protected $mapper;
    
    public function authenticate(AuthenticationEvent $event)
    {
        if ($this->isSatisfied()) {
            $storage = $this->getStorage()->read();
            $event->setIdentity($storage['identity'])
                ->setCode(AuthenticationResult::SUCCESS)
                ->setMessages(array('Authentication successful.'));
            return;
        }

        $identity   = $event->getRequest()->getPost()->get('identity');
        $credential = $event->getRequest()->getPost()->get('credential');
        $credential = $this->preProcessCredential($credential);
        $userObject = null;

        // Cycle through the configured identity sources and test each
        $fields = $this->getOptions()->getAuthIdentityFields();
        while (!is_object($userObject) && count($fields) > 0) {
            $mode = array_shift($fields);
            switch ($mode) {
                case 'username':
                    $userObject = $this->getMapper()->findAdminByUsername($identity);
                    break;
                case 'email':
                    $userObject = $this->getMapper()->findAdminByEmail($identity);
                    break;
            }
        }
        
        if (!$userObject) {
            $event->setCode(AuthenticationResult::FAILURE_IDENTITY_NOT_FOUND)
                ->setMessages(array('A record with the supplied identity could not be found.'));
            $this->setSatisfied(false);
            return false;
        }

        if ($this->getOptions()->getEnableUserState()) {
            // Don't allow user to login if state is not in allowed list
            if (!in_array($userObject->getState(), $this->getOptions()->getAllowedLoginStates())) {
                $event->setCode(AuthenticationResult::FAILURE_UNCATEGORIZED)
                    ->setMessages(array('A record with the supplied identity is not active.'));
                $this->setSatisfied(false);
                return false;
            }
        }

        $cryptoService = $this->getHydrator()->getCryptoService();
        if (!$cryptoService->verify($credential, $userObject->getPassword())) {
            // Password does not match
            $event->setCode(AuthenticationResult::FAILURE_CREDENTIAL_INVALID)
                ->setMessages(array('Supplied credential is invalid.'));
            $this->setSatisfied(false);
            return false;
        } elseif ($cryptoService instanceof Bcrypt) {
            // Update user's password hash if the cost parameter has changed
            $this->updateUserPasswordHash($userObject, $credential, $cryptoService);
        }

        // regen the id
        SessionContainer::getDefaultManager()->regenerateId();

        // Success!
        $event->setIdentity($userObject->getUserId());

        $this->setSatisfied(true);
        $storage = $this->getStorage()->read();
        $storage['identity'] = $event->getIdentity();
        $this->getStorage()->write($storage);
        $event->setCode(AuthenticationResult::SUCCESS);
    }   
    
     /**
     * getMapper
     *
     * @return UserMapper
     */
    public function getMapper()
    {
        if (!$this->mapper instanceof UserMapper) {
            $this->setMapper($this->serviceManager->get('admin_user_mapper'));
        }
        return $this->mapper;
    }

    /**
     * setMapper
     *
     * @param UserMapper $mapper
     * @return Db
     */
    public function setMapper(AdminMapper $mapper)
    {
        $this->mapper = $mapper;
        return $this;
    }
}
